package com.rtsapp.server.network.protocol.rpc.server;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

/**
 * 一个RPC服务类信息
 */
class RPCServiceInfo {

    /**
     * 对象
     */
    private final Object object;


    /**
     * 所有方法存储的map
     * 方法名->该方法名对应的所有方法数组( 每个方法用一组参数来标识 )
     */
    private final Map<String, MethodInfo[]> methodsMap = new HashMap<>();


    public RPCServiceInfo(Object object) {

        this.object = object;

        initMethodInfo(object);
    }


    //TODO  我觉得远程方法调用可以不支持方法重载, 这样既能稍微提高性能, 又能保证代码简单
    private void initMethodInfo(Object object) {

        Method[] methods = object.getClass().getDeclaredMethods();

        for (Method m : methods) {

            //只接受public方法
            if( ! Modifier.isPublic( m.getModifiers() ) ){
                continue;
            }

            String methodName = m.getName();
            MethodInfo[] methodInfos = methodsMap.get(methodName);

            if (methodInfos == null) {
                methodsMap.put(methodName, new MethodInfo[]{new MethodInfo(m)});
            } else {
                MethodInfo[] newMethodInfos = new MethodInfo[methodInfos.length + 1];
                System.arraycopy(methodInfos, 0, newMethodInfos, 0, methodInfos.length);
                newMethodInfos[methodInfos.length] = new MethodInfo(m);

                methodsMap.put(methodName, newMethodInfos);
            }
        }
    }

    /**
     * 调用该对象的方法
     *
     * @param methodName
     * @param parameters
     * @return
     */
    public Object invoke(String methodName, Object[] parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        MethodInfo[] methodInfos = methodsMap.get(methodName);
        if (methodInfos == null) {
            throw new NoSuchMethodException(methodName + "." + argumentTypesToString(parameters));
        }

        for (int i = 0; i < methodInfos.length; i++) {
            if (methodInfos[i].isParamsCompatible(parameters)) {
                return methodInfos[i].getMethod().invoke(object, parameters);
            }
        }

        throw new NoSuchMethodException(methodName + "." + argumentTypesToString(parameters));

    }


    private String argumentTypesToString(Object[] parameters) {
        Class[] classes = new Class[parameters.length];

        for (int i = 0; i < parameters.length; i++) {
            classes[i] = parameters[i].getClass();
        }

        StringBuilder buf = new StringBuilder();
        buf.append("(");

        for (int i = 0; i < classes.length; i++) {
            if (i > 0) {
                buf.append(", ");
            }
            Class<?> c = classes[i];
            buf.append((c == null) ? "null" : c.getName());
        }
        buf.append(")");

        return buf.toString();
    }

}


/**
 * 方法信息
 */
class MethodInfo {

    private final Method method;
    private final Class<?>[] paramterTypes;
    private final ITypeCompare[] paramterTypeCompares;

    public MethodInfo(Method method) {
        this.method = method;
        this.paramterTypes = method.getParameterTypes();

        paramterTypeCompares = new ITypeCompare[paramterTypes.length];
        for (int i = 0; i < paramterTypes.length; i++) {
            paramterTypeCompares[i] = TypeCompareFactory.getTypeCompare(paramterTypes[i]);
        }
    }

    /**
     * 判断一组参数是否和该方法的定义兼容
     * 1. 如果参数个数不一致，不兼容
     * 2. 对于每个参数
     * 1. 如果参数类型是类或接口，传入null，是兼容的
     * 2. 如果参数类型是类或接口，传入的是子类，是兼容的
     * <p>
     * 3. 如果参数类型是基本类型, 传入的是对应的包装类型，是兼容的
     *
     * @param parameters
     * @return
     */
    public boolean isParamsCompatible(Object[] parameters) {

        if (parameters.length != paramterTypes.length) {
            return false;
        }

        for (int i = 0; i < paramterTypes.length; i++) {
            if (!paramterTypeCompares[i].isTypeCompatible(parameters[i])) {
                return false;
            }
        }
        return true;
    }

    public Method getMethod() {
        return method;
    }
}


/**
 * 类型比较器
 */
interface ITypeCompare {
    /**
     * 类型是否是兼容的
     *
     * @param param
     * @return
     */
    boolean isTypeCompatible(Object param);
}


/**
 * 对象类型判断
 * 1. 如果参数是null，是兼容的
 * 2. 如果参数是该类对象或者子类对象，是兼容的
 */
class ObjectTypeCompare implements ITypeCompare {

    private final Class<?> type;

    public ObjectTypeCompare(Class<?> type) {
        this.type = type;
    }

    public boolean isTypeCompatible(Object param) {
        if (param == null) {
            return true;
        }
        return type.isInstance(param);
    }

}

/**
 * 基本类型的判断
 * 1. 如果传入null，表示不兼容
 * 2. 如果传入的是对应的包装类型，是兼容的
 *
 * @param
 */
class BasicTypeCompare implements ITypeCompare {


    private final Class type;

    BasicTypeCompare( Class type) {
        this.type = type;
    }


    @Override
    public boolean isTypeCompatible(Object param) {
        if (param == null) {
            return false;
        }

        return (param.getClass() == type);
    }

}

class TypeCompareFactory {

    private static BasicTypeCompare charCompare = new BasicTypeCompare(Character.class);

    private static BasicTypeCompare booleanCompare = new BasicTypeCompare( Boolean.class );

    private static BasicTypeCompare byteCompare = new BasicTypeCompare( Byte.class );
    private static BasicTypeCompare shortCompare = new BasicTypeCompare( Short.class );
    private static BasicTypeCompare intCompare = new BasicTypeCompare( Integer.class );
    private static BasicTypeCompare longCompare = new BasicTypeCompare( Long.class );

    private static BasicTypeCompare floatCompare = new BasicTypeCompare( Float.class );
    private static BasicTypeCompare doubleCompare = new BasicTypeCompare( Double.class );


    public static ITypeCompare getTypeCompare(Class<?> parameterType) {

        if (parameterType == char.class) {
            return charCompare;
        } else if (parameterType == boolean.class) {
            return booleanCompare;
        } else if (parameterType == byte.class) {
            return byteCompare;
        } else if (parameterType == short.class) {
            return shortCompare;
        } else if (parameterType == int.class) {
            return intCompare;
        } else if (parameterType == long.class) {
            return longCompare;
        } else if (parameterType == float.class) {
            return floatCompare;
        } else if (parameterType == double.class) {
            return doubleCompare;
        }

        return new ObjectTypeCompare(parameterType);
    }

}